/* eslint-disable @typescript-eslint/no-explicit-any */
/*
 * @Author: Hp_lucky 1063512385@qq.com
 * @Date: 2024-08-28 11:36:47
 * @LastEditors: Hp_lucky 1063512385@qq.com
 * @LastEditTime: 2024-09-11 10:48:26
 * @FilePath: \cocos_frame\assets\script\src\Basic\Component\PoolFactory.ts
 */
import { NodePool, instantiate, Component, _decorator, Node, Prefab } from 'cc';
import { NodeRecycle } from '../Manager/PoolManager';
const { ccclass, property, menu } = _decorator;

@ccclass('PoolFactory')
@menu('myComponent/PoolFactory')
export class PoolFactory extends Component {
  @property([Prefab])
  prefabs: Prefab[] = [];
  @property([Node])
  renders: Node[] = [];
  /** 节点工厂 */
  private _nodeRenders: Map<string, Node> = new Map<string, Node>();
  private _nodePool: Map<string, NodePool> = new Map<string, NodePool>();

  protected onLoad(): void {
    this.refreshPool();
  }

  refreshPool() {
    this.prefabs.forEach((prefab) => {
      if (prefab == null || this._nodeRenders.has(prefab.name)) return;
      const pool = new NodePool();
      this._nodePool.set(prefab.name, pool);
      const nn = instantiate(prefab);
      this._nodeRenders.set(prefab.name, nn);
    });
    this.renders.forEach((node) => {
      if (node == null || this._nodeRenders.has(node.name)) return;
      const pool = new NodePool();
      this._nodePool.set(node.name, pool);
      node.removeFromParent();
      this._nodeRenders.set(node.name, node);
    });
  }

  /**
   * 从对象池中获取指定类型的Node
   * @param type 指定的类型
   * @param bundle 指定的分包
   * @param path 指定的资源路径
   * @returns 实例化后的Node
   */
  pop(nodeName: string, ...args: unknown[]): Node {
    const pool = this._nodePool.get(nodeName);
    let node: Node;
    let sp: NodeRecycle;
    if (pool == null || pool.size() <= 0) {
      if (!this._nodeRenders.has(nodeName)) throw new Error('节点工厂不存在节点模板：' + nodeName);
      node = instantiate(this._nodeRenders.get(nodeName));
      sp = node.getComponent(NodeRecycle as any);
      if (sp) sp.factory = this;
    } else {
      node = pool.get();
      sp = node.getComponent(NodeRecycle as any);
    }
    if (sp) sp.reuse(...args);
    return node;
  }

  /**
   * 把指定类型的Node放回对象池
   * @param type 指定的类型
   * @param node 指定的Node
   */
  push(node: Node) {
    if (!this._nodePool.has(node.name)) throw new Error(`节点 ${node.name} 没有对应的对象池`);
    this._nodePool.get(node.name).put(node);
  }

  /**
   * 把指定类型的Node放回对象池
   * @param type 指定的类型
   * @param node 指定的Node
   */
  pushList(node: Node[]) {
    const nodeName = node[0].name;
    if (!this._nodePool.has(nodeName)) throw new Error(`节点 ${nodeName} 没有对应的对象池`);
    node.forEach((item) => {
      this._nodePool.get(nodeName).put(item);
    });
  }

  onDestroy() {
    this._nodeRenders.forEach((node) => {
      node.destroy();
    });
    this._nodePool.forEach((pool) => {
      pool.clear();
    });
  }
}
